library(ggplot2)
library(dplyr)
library(tidyr)
library(reshape2)
library(RColorBrewer)
library(knitr)
library(kableExtra)

In May 2017 I went on a 5 week trip around Europe. Yes, it was nice. Of course it was! I took a lot of photos, so obviously when I got back I wrote some simple code to have a look at my photographic habits inspired by this post. I used exiftool to dump some data into a csv and then I pulled it all into R to see whether my patterns were strong enough to either change how I shoot or change my kit.

Why am I doing this?

I only had one lens on me at the time, and I often found myself in camera shops because I felt my lens was limiting the kind of photos I could take. So I figured I might look at the exif data from the photos I took and see whether I was bumping up against constraints imposed by the kit zoom lens I was using. So the question I’m trying to answer is:

Is my lens holding me back?

Do I have an excuse (uh … reason) to buy a new one?

What am I looking for?

The key features in a lens are the focal length and the maximum size of the aperture (the opening that lets in the light), so I’ll focus on those. I was using a kit zoom lens with my Sony a6000 which can shoot with a focal length of 16-50mm and an aperture between f3.5 and f5.6 depending on how much you have zoomed in (see the chart further down).

I like taking pictures of architecture, and I also like shooting at night so I tend to want a bigger aperture (smaller f number) and wider frame (short focal length). But whether my impression is correct or not will be revealed by the data.


One of my masterpieces
A very nervous looking lion as seen out the front of the Pergamon Museum in Berlin

Note: I use a fair bit of photography jargon here. Aperture is particularly counterintuitive because a large opening, letting a lot of light onto the sensor, has a small f-stop value. See this quora question for more detail.

Get the data out of exiftool

The first thing to do is to get the exif data. Exiftool has lots and lots of options for getting specific data out which can give you the exact data you’re after, so I requested a few variables relating to aperture and focal length (see the code snippet). I used the exiftool command prompt to put the exif data into a csv, and then read it into R.

PS: I only found out later that there’s an R package called exifr that would have made this a bit easier. You can basically do what my code does except go straight from exiftool to a dataframe, rather than via a CSV. Who would say no to that?! If I repeat this with another batch of snaps I’ll definitely use exifr.

system2(command = 'exiftool', 
        args = c('-csv', '-r',
                 '-DateTimeOriginal', '-Model',
                 '-MaxApertureValue', '-Aperture', '-FocalLength',
                 paste0('"/Volumes/SO SPACE!/Photos/17-05"*')),
        stdout = './inputs/my_photos.csv')
exif_raw <- read.csv('./inputs/my_photos.csv', stringsAsFactors = F)

Clean

To begin with I’ll remove any image entries that don’t have a complete set of the data I’m looking at. But for peace of mind we’ll take a look at what was removed.

kable(exif_raw[!complete.cases(exif_raw),]) %>% 
  kable_styling(bootstrap_options = c("bordered") , full_width = F)
SourceFile DateTimeOriginal Model MaxApertureValue Aperture FocalLength
1466 /Volumes/SO SPACE!/Photos/17-05-21 Vienna/MAH05953.MP4 NA NA

It looks like the only incomplete data point we picked up in the initial batch of data was a movie file (.mp4), and I’m not particularly interested in that so I’m happy to let it go.

First Glance

Seasoned photographers will recognise here that there’s an inherent tension between aperture and focal length. I’ll spare you the physics and give you a chart instead:

The squares that sit at the bottom of the chart mark the minimum f-stop (which corresponds to the largest aperture size) at a given focal length. What you can see is that the fstop increases slowly as you go to a higher focal length.

There are a few things raised by this chart:

  1. I got to try an Olympus Pen with a 17mm f1.8 prime lens on a tour I did in Frankfurt, and it demonstrates pretty clearly what a new piece of kit can do to expand your creative scope. 1
  2. You can see that I took a fair amount of photos at the lower apertures, but the colour for the dots at 16mm and 50mm is the darkest, and not just at lower apertures.

We can tell that the main game here is focal length, and that aperture is less important (at least for this batch). To get a more accurate picture of exactly how most of my photos are taken I created the same plot but just for the Sony with kit lens. The colouring on the heat map gives a fair bit more resolution on the photo density at each combination of focal length and aperture.

exif_fl_ap <- fl_aperture %>% 
  filter(Source == "Photo",
         Model == "A6000 (+Sony 16-50mm f3.5-5.6)") %>% 
  mutate(FocalLength_bin = round(FocalLength),
         Aperture_bin = Aperture) %>% 
  dcast(FocalLength_bin~Aperture_bin, value.var = "Aperture") %>% 
  melt(id = c("FocalLength_bin"), 
       variable.name = "Aperture_bin",
       value.name = "Count")
Aggregation function missing: defaulting to length

Well if I was looking for an indication of my constraints I think I found it. The vast majority of my photos seem to be taken at 16mm on the kit lens:

The relative proportions here are almost rediculous, so I guess I have my answer 😎

Some final remarks

There’s a lot more to dig into here. For one, I deliberately keep my aperture in the 5.6 - 8 range because that’s where I get the best image quality, so seeing a large number of shots at 3.5 still raises my eyebrow slightly. Maybe I should get a wide angle lens with a large aperture? Sounds expensive …

On the data front though, there’s a lot more on offer in exiftool. I haven’t looked at how I could better use ISO or how my photography style has changed over time, next time …


  1. I have corrected both the aperture and focal length calculations for the effect of sensor size to get an apples to apples comparison between the Sony and the Olympus. In this case the micro 4/3 numbers were multiplied by 2/1.53 to convert them to an APS-C equivalent number.↩

LS0tCnRpdGxlOiAiQW5hbHlzaW5nIG15IHBob3RvcyB3aXRoIFIgYW5kIEV4aWZ0b29sIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7ciBMb2FkIExpYnJhcmllcywgZWNobz1UUlVFfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmBgYAoKCkluIE1heSAyMDE3IEkgd2VudCBvbiBhIDUgd2VlayB0cmlwIGFyb3VuZCBFdXJvcGUuIFllcywgaXQgd2FzIG5pY2UuIE9mIGNvdXJzZSBpdCB3YXMhIEkgdG9vayBhIGxvdCBvZiBwaG90b3MsIHNvIG9idmlvdXNseSB3aGVuIEkgZ290IGJhY2sgSSB3cm90ZSBzb21lIHNpbXBsZSBjb2RlIHRvIGhhdmUgYSBsb29rIGF0IG15IHBob3RvZ3JhcGhpYyBoYWJpdHMgaW5zcGlyZWQgYnkgW3RoaXMgcG9zdF0oaHR0cHM6Ly90aW1lbHlwb3J0Zm9saW8uZ2l0aHViLmlvL3JDaGFydHNfY2F0Y29ycmpzL2V4aWYvKS4gSSB1c2VkIFtleGlmdG9vbF0oaHR0cHM6Ly93d3cuc25vLnBoeS5xdWVlbnN1LmNhL35waGlsL2V4aWZ0b29sLykgdG8gZHVtcCBzb21lIGRhdGEgaW50byBhIGNzdiBhbmQgdGhlbiBJIHB1bGxlZCBpdCBhbGwgaW50byBSIHRvIHNlZSB3aGV0aGVyIG15IHBhdHRlcm5zIHdlcmUgc3Ryb25nIGVub3VnaCB0byBlaXRoZXIgY2hhbmdlIGhvdyBJIHNob290IG9yIGNoYW5nZSBteSBraXQuCgojIyMgV2h5IGFtIEkgZG9pbmcgdGhpcz8KCkkgb25seSBoYWQgb25lIGxlbnMgb24gbWUgYXQgdGhlIHRpbWUsIGFuZCBJIG9mdGVuIGZvdW5kIG15c2VsZiBpbiBjYW1lcmEgc2hvcHMgYmVjYXVzZSBJIGZlbHQgbXkgbGVucyB3YXMgbGltaXRpbmcgdGhlIGtpbmQgb2YgcGhvdG9zIEkgY291bGQgdGFrZS4gU28gSSBmaWd1cmVkIEkgbWlnaHQgbG9vayBhdCB0aGUgZXhpZiBkYXRhIGZyb20gdGhlIHBob3RvcyBJIHRvb2sgYW5kIHNlZSB3aGV0aGVyIEkgd2FzIGJ1bXBpbmcgdXAgYWdhaW5zdCBjb25zdHJhaW50cyBpbXBvc2VkIGJ5IHRoZSBraXQgem9vbSBsZW5zIEkgd2FzIHVzaW5nLiBTbyB0aGUgcXVlc3Rpb24gSSdtIHRyeWluZyB0byBhbnN3ZXIgaXM6Cgo+IElzIG15IGxlbnMgaG9sZGluZyBtZSBiYWNrPyAKPgo+IERvIEkgaGF2ZSBhbiBleGN1c2UgKHVoIC4uLiByZWFzb24pIHRvIGJ1eSBhIG5ldyBvbmU/CgojIyMgV2hhdCBhbSBJIGxvb2tpbmcgZm9yPwpUaGUga2V5IGZlYXR1cmVzIGluIGEgbGVucyBhcmUgdGhlIGZvY2FsIGxlbmd0aCBhbmQgdGhlIG1heGltdW0gc2l6ZSBvZiB0aGUgYXBlcnR1cmUgKHRoZSBvcGVuaW5nIHRoYXQgbGV0cyBpbiB0aGUgbGlnaHQpLCBzbyBJJ2xsIGZvY3VzIG9uIHRob3NlLiBJIHdhcyB1c2luZyBhIGtpdCB6b29tIGxlbnMgd2l0aCBteSBTb255IGE2MDAwIHdoaWNoIGNhbiBzaG9vdCB3aXRoIGEgZm9jYWwgbGVuZ3RoIG9mIDE2LTUwbW0gYW5kIGFuIGFwZXJ0dXJlIGJldHdlZW4gZjMuNSBhbmQgZjUuNiBkZXBlbmRpbmcgb24gaG93IG11Y2ggeW91IGhhdmUgem9vbWVkIGluIChzZWUgdGhlIGNoYXJ0IGZ1cnRoZXIgZG93bikuCgpJIGxpa2UgdGFraW5nIHBpY3R1cmVzIG9mIGFyY2hpdGVjdHVyZSwgYW5kIEkgYWxzbyBsaWtlIHNob290aW5nIGF0IG5pZ2h0IHNvIEkgdGVuZCB0byB3YW50IGEgYmlnZ2VyIGFwZXJ0dXJlIChzbWFsbGVyIGYgbnVtYmVyKSBhbmQgd2lkZXIgZnJhbWUgKHNob3J0IGZvY2FsIGxlbmd0aCkuIEJ1dCB3aGV0aGVyIG15IGltcHJlc3Npb24gaXMgY29ycmVjdCBvciBub3Qgd2lsbCBiZSByZXZlYWxlZCBieSB0aGUgZGF0YS4KCjxjZW50ZXI+Cjxicj4KPGltZyBzcmM9Ii4vcmVmL05lcnZvdXNMaW9uLnBuZyIgYWx0PSJPbmUgb2YgbXkgbWFzdGVycGllY2VzIiB3aWR0aD0iNzUwcHgiIC8+Cjxicj4KKkEgdmVyeSBuZXJ2b3VzIGxvb2tpbmcgbGlvbiBhcyBzZWVuIG91dCB0aGUgZnJvbnQgb2YgdGhlIFBlcmdhbW9uIE11c2V1bSBpbiBCZXJsaW4qCjxicj48YnI+CjwvY2VudGVyPgoKKk5vdGU6KiBJIHVzZSBhIGZhaXIgYml0IG9mIHBob3RvZ3JhcGh5IGphcmdvbiBoZXJlLiBBcGVydHVyZSBpcyBwYXJ0aWN1bGFybHkgY291bnRlcmludHVpdGl2ZSBiZWNhdXNlIGEgbGFyZ2Ugb3BlbmluZywgbGV0dGluZyBhIGxvdCBvZiBsaWdodCBvbnRvIHRoZSBzZW5zb3IsIGhhcyBhIHNtYWxsIGYtc3RvcCB2YWx1ZS4gU2VlIFt0aGlzIHF1b3JhIHF1ZXN0aW9uXShodHRwOi8vYml0LmRvL2V0dmo5KSBmb3IgbW9yZSBkZXRhaWwuCgoKIyMjIEdldCB0aGUgZGF0YSBvdXQgb2YgKmV4aWZ0b29sKgoKVGhlIGZpcnN0IHRoaW5nIHRvIGRvIGlzIHRvIGdldCB0aGUgZXhpZiBkYXRhLiBFeGlmdG9vbCBoYXMgbG90cyBhbmQgbG90cyBvZiBbb3B0aW9ucyBmb3IgZ2V0dGluZyBzcGVjaWZpYyBkYXRhIG91dF0oaHR0cHM6Ly93d3cuc25vLnBoeS5xdWVlbnN1LmNhL35waGlsL2V4aWZ0b29sL2V4aWZ0b29sX3BvZC5odG1sI1JFQURJTkctRVhBTVBMRVMpIHdoaWNoIGNhbiBnaXZlIHlvdSB0aGUgZXhhY3QgZGF0YSB5b3UncmUgYWZ0ZXIsIHNvIEkgcmVxdWVzdGVkIGEgZmV3IHZhcmlhYmxlcyByZWxhdGluZyB0byBhcGVydHVyZSBhbmQgZm9jYWwgbGVuZ3RoIChzZWUgdGhlIGNvZGUgc25pcHBldCkuIApJIHVzZWQgdGhlIGV4aWZ0b29sIGNvbW1hbmQgcHJvbXB0IHRvIHB1dCB0aGUgZXhpZiBkYXRhIGludG8gYSBjc3YsIGFuZCB0aGVuIHJlYWQgaXQgaW50byBSLgoKKlBTOiogSSBvbmx5IGZvdW5kIG91dCBsYXRlciB0aGF0IHRoZXJlJ3MgYW4gUiBwYWNrYWdlIGNhbGxlZCBbYGV4aWZyYF0oaHR0cHM6Ly9naXRodWIuY29tL3BhbGVvbGltYm90L2V4aWZyKSB0aGF0IHdvdWxkIGhhdmUgbWFkZSB0aGlzIGEgYml0IGVhc2llci4gWW91IGNhbiBiYXNpY2FsbHkgZG8gd2hhdCBteSBjb2RlIGRvZXMgZXhjZXB0IGdvIHN0cmFpZ2h0IGZyb20gZXhpZnRvb2wgdG8gYSBkYXRhZnJhbWUsIHJhdGhlciB0aGFuIHZpYSBhIENTVi4gV2hvIHdvdWxkIHNheSBubyB0byB0aGF0PyEgSWYgSSByZXBlYXQgdGhpcyB3aXRoIGFub3RoZXIgYmF0Y2ggb2Ygc25hcHMgSSdsbCBkZWZpbml0ZWx5IHVzZSBleGlmci4KCmBgYHtyIEdlbmVyYXRlIEV4aWYgRGF0YSBhbmQgSW1wb3J0fQpzeXN0ZW0yKGNvbW1hbmQgPSAnZXhpZnRvb2wnLCAKICAgICAgICBhcmdzID0gYygnLWNzdicsICctcicsCiAgICAgICAgICAgICAgICAgJy1EYXRlVGltZU9yaWdpbmFsJywgJy1Nb2RlbCcsCiAgICAgICAgICAgICAgICAgJy1NYXhBcGVydHVyZVZhbHVlJywgJy1BcGVydHVyZScsICctRm9jYWxMZW5ndGgnLAogICAgICAgICAgICAgICAgIHBhc3RlMCgnIi9Wb2x1bWVzL1NPIFNQQUNFIS9QaG90b3MvMTctMDUiKicpKSwKICAgICAgICBzdGRvdXQgPSAnLi9pbnB1dHMvbXlfcGhvdG9zLmNzdicpCmV4aWZfcmF3IDwtIHJlYWQuY3N2KCcuL2lucHV0cy9teV9waG90b3MuY3N2Jywgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmBgYAoKIyMjIENsZWFuCgpUbyBiZWdpbiB3aXRoIEknbGwgcmVtb3ZlIGFueSBpbWFnZSBlbnRyaWVzIHRoYXQgZG9uJ3QgaGF2ZSBhIGNvbXBsZXRlIHNldCBvZiB0aGUgZGF0YSBJJ20gbG9va2luZyBhdC4gQnV0IGZvciBwZWFjZSBvZiBtaW5kIHdlJ2xsIHRha2UgYSBsb29rIGF0IHdoYXQgd2FzIHJlbW92ZWQuCgpgYGB7ciBlY2hvPVRSVUUsIHJlc3VsdHM9J2FzaXMnfQpleGlmX2NsZWFuIDwtIGV4aWZfcmF3W2NvbXBsZXRlLmNhc2VzKGV4aWZfcmF3KSxdICU+JSAKICBtdXRhdGUoRm9jYWxMZW5ndGggPSBhcy5udW1lcmljKHN1YnN0cihGb2NhbExlbmd0aCwgMSwgcmVnZXhwcigiKCBtKSIsIEZvY2FsTGVuZ3RoKS0xKSkpCgprYWJsZShleGlmX3Jhd1shY29tcGxldGUuY2FzZXMoZXhpZl9yYXcpLF0pICU+JSAKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygiYm9yZGVyZWQiKSAsIGZ1bGxfd2lkdGggPSBGKQpgYGAKCkl0IGxvb2tzIGxpa2UgdGhlIG9ubHkgaW5jb21wbGV0ZSBkYXRhIHBvaW50IHdlIHBpY2tlZCB1cCBpbiB0aGUgaW5pdGlhbCBiYXRjaCBvZiBkYXRhIHdhcyBhIG1vdmllIGZpbGUgKC5tcDQpLCBhbmQgSSdtIG5vdCBwYXJ0aWN1bGFybHkgaW50ZXJlc3RlZCBpbiB0aGF0IHNvIEknbSBoYXBweSB0byBsZXQgaXQgZ28uCgojIyMgRmlyc3QgR2xhbmNlClNlYXNvbmVkIHBob3RvZ3JhcGhlcnMgd2lsbCByZWNvZ25pc2UgaGVyZSB0aGF0IHRoZXJlJ3MgYW4gaW5oZXJlbnQgdGVuc2lvbiBiZXR3ZWVuIGFwZXJ0dXJlIGFuZCBmb2NhbCBsZW5ndGguIEknbGwgc3BhcmUgeW91IHRoZSBwaHlzaWNzIGFuZCBnaXZlIHlvdSBhIGNoYXJ0IGluc3RlYWQ6CmBgYHtyIFBsb3QgTWF4IEFwZXJ0dXJlIFZhbHVlLCBlY2hvPVRSVUV9CmZsX2FwZXJ0dXJlX2FsbCA8LSBleGlmX2NsZWFuICU+JQogIHNlbGVjdCgtU291cmNlRmlsZSwgLURhdGVUaW1lT3JpZ2luYWwpICU+JQogIG11dGF0ZShBcGVydHVyZSA9IGlmZWxzZShNb2RlbCA9PSAiSUxDRS02MDAwIiwgQXBlcnR1cmUsIEFwZXJ0dXJlKjIvMS41MyksCiAgICAgICAgIE1heEFwZXJ0dXJlVmFsdWUgPSBpZmVsc2UoTW9kZWwgPT0gIklMQ0UtNjAwMCIsIE1heEFwZXJ0dXJlVmFsdWUsIE1heEFwZXJ0dXJlVmFsdWUqMi8xLjUzKSwKICAgICAgICAgRm9jYWxMZW5ndGggPSBpZmVsc2UoTW9kZWwgPT0gIklMQ0UtNjAwMCIsIEZvY2FsTGVuZ3RoLCBGb2NhbExlbmd0aCoyLzEuNTMpLAogICAgICAgICBNb2RlbCA9IGlmZWxzZShNb2RlbCA9PSAiSUxDRS02MDAwIiwgIkE2MDAwICgrU29ueSAxNi01MG1tIGYzLjUtNS42KSIsICJQZW4tRiAoK09seW1wdXMgMTdtbSBmMS44KSIpKQoKZmxfYXBlcnR1cmVfYWN0IDwtIGZsX2FwZXJ0dXJlX2FsbCAlPiUgCiAgc2VsZWN0KC1NYXhBcGVydHVyZVZhbHVlKSAlPiUgCiAgZ3JvdXBfYnkoTW9kZWwpICU+JSAKICBtdXRhdGUoU291cmNlID0gJ1Bob3RvJykKCmZsX2FwZXJ0dXJlX21heCA8LSBmbF9hcGVydHVyZV9hbGwgJT4lIAogIHNlbGVjdCgtQXBlcnR1cmUpICU+JSAKICBncm91cF9ieShNb2RlbCwgRm9jYWxMZW5ndGgpICU+JSAKICBzdW1tYXJpc2UoQXBlcnR1cmUgPSBtaW4oTWF4QXBlcnR1cmVWYWx1ZSkpICU+JSAKICBtdXRhdGUoU291cmNlID0gJ01heCBBcGVydHVyZScpCgpmbF9hcGVydHVyZSA8LSByYmluZChmbF9hcGVydHVyZV9hY3QsIGZsX2FwZXJ0dXJlX21heCkKeF9icmVha3MgPC0gc2VxKDE2LCA1MCwgYnkgPSAyKQoKc2NhdHRlcl9wbG90IDwtIGZsX2FwZXJ0dXJlICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBGb2NhbExlbmd0aCwgeSA9IEFwZXJ0dXJlLCBjb2xvdXIgPSBNb2RlbCwgYWxwaGEgPSBTb3VyY2UsIHNoYXBlID0gU291cmNlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0geF9icmVha3MpICsgCiAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoMSwwLjA1KSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDAsMTYpKSArCiAgbGFicyh4ID0gIkZvY2FsIExlbmd0aCAobW0gQVBTLUMgRXF1aXZhbGVudCkiLAogICAgICAgeSA9ICJmLXN0b3AvQXBlcnR1cmUgKHNtYWxsZXIgbnVtYmVyIC0+IG1vcmUgbGlnaHQpIikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxNiwgNTApLCB5bGltID0gYygwLCAzMCkpCmBgYAoKYGBge3IgUGxvdH0Kc2NhdHRlcl9wbG90CmBgYApUaGUgc3F1YXJlcyB0aGF0IHNpdCBhdCB0aGUgYm90dG9tIG9mIHRoZSBjaGFydCBtYXJrIHRoZSBtaW5pbXVtIGYtc3RvcCAod2hpY2ggY29ycmVzcG9uZHMgdG8gdGhlIGxhcmdlc3QgYXBlcnR1cmUgc2l6ZSkgYXQgYSBnaXZlbiBmb2NhbCBsZW5ndGguIFdoYXQgeW91IGNhbiBzZWUgaXMgdGhhdCB0aGUgZnN0b3AgaW5jcmVhc2VzIHNsb3dseSBhcyB5b3UgZ28gdG8gYSBoaWdoZXIgZm9jYWwgbGVuZ3RoLiAKClRoZXJlIGFyZSBhIGZldyB0aGluZ3MgcmFpc2VkIGJ5IHRoaXMgY2hhcnQ6CgoxLiBJIGdvdCB0byB0cnkgYW4gT2x5bXB1cyBQZW4gd2l0aCBhIDE3bW0gZjEuOCBwcmltZSBsZW5zIG9uIGEgdG91ciBJIGRpZCBpbiBGcmFua2Z1cnQsIGFuZCBpdCBkZW1vbnN0cmF0ZXMgcHJldHR5IGNsZWFybHkgd2hhdCBhIG5ldyBwaWVjZSBvZiBraXQgY2FuIGRvIHRvIGV4cGFuZCB5b3VyIGNyZWF0aXZlIHNjb3BlLiBbXjFdCjIuIFlvdSBjYW4gc2VlIHRoYXQgSSB0b29rIGEgZmFpciBhbW91bnQgb2YgcGhvdG9zIGF0IHRoZSBsb3dlciBhcGVydHVyZXMsIGJ1dCB0aGXCoGNvbG91ciBmb3IgdGhlIGRvdHMgYXQgMTZtbSBhbmQgNTBtbSBpcyB0aGUgZGFya2VzdCwgYW5kIG5vdCBqdXN0IGF0IGxvd2VyIGFwZXJ0dXJlcy4KCldlIGNhbiB0ZWxsIHRoYXQgdGhlIG1haW4gZ2FtZSBoZXJlIGlzIGZvY2FsIGxlbmd0aCwgYW5kIHRoYXQgYXBlcnR1cmUgaXMgbGVzcyBpbXBvcnRhbnQgKGF0IGxlYXN0IGZvciB0aGlzIGJhdGNoKS4gVG8gZ2V0IGEgbW9yZSBhY2N1cmF0ZSBwaWN0dXJlIG9mIGV4YWN0bHkgaG93IG1vc3Qgb2YgbXkgcGhvdG9zIGFyZSB0YWtlbiBJIGNyZWF0ZWQgdGhlIHNhbWUgcGxvdCBidXQganVzdCBmb3IgdGhlIFNvbnkgd2l0aCBraXQgbGVucy4gVGhlIGNvbG91cmluZyBvbiB0aGUgaGVhdCBtYXAgZ2l2ZXMgYSBmYWlyIGJpdCBtb3JlIHJlc29sdXRpb24gb24gdGhlIHBob3RvIGRlbnNpdHkgYXQgZWFjaCBjb21iaW5hdGlvbiBvZiBmb2NhbCBsZW5ndGggYW5kIGFwZXJ0dXJlLgoKYGBge3IgSGVhdG1hcH0KZXhpZl9mbF9hcCA8LSBmbF9hcGVydHVyZSAlPiUgCiAgZmlsdGVyKFNvdXJjZSA9PSAiUGhvdG8iLAogICAgICAgICBNb2RlbCA9PSAiQTYwMDAgKCtTb255IDE2LTUwbW0gZjMuNS01LjYpIikgJT4lIAogIG11dGF0ZShGb2NhbExlbmd0aF9iaW4gPSByb3VuZChGb2NhbExlbmd0aCksCiAgICAgICAgIEFwZXJ0dXJlX2JpbiA9IEFwZXJ0dXJlKSAlPiUgCiAgZGNhc3QoRm9jYWxMZW5ndGhfYmlufkFwZXJ0dXJlX2JpbiwgdmFsdWUudmFyID0gIkFwZXJ0dXJlIikgJT4lIAogIG1lbHQoaWQgPSBjKCJGb2NhbExlbmd0aF9iaW4iKSwgCiAgICAgICB2YXJpYWJsZS5uYW1lID0gIkFwZXJ0dXJlX2JpbiIsCiAgICAgICB2YWx1ZS5uYW1lID0gIkNvdW50IikKCmhlYXRtYXBfZmxfYXAgPC0gZXhwYW5kLmdyaWQoRm9jYWxMZW5ndGggPSBzZXEobWluKGV4aWZfZmxfYXAkRm9jYWxMZW5ndGhfYmluKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGV4aWZfZmxfYXAkRm9jYWxMZW5ndGhfYmluKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBcGVydHVyZSA9IHVuaXF1ZShleGlmX2ZsX2FwJEFwZXJ0dXJlX2JpbikpICU+JSAKICBsZWZ0X2pvaW4oZXhpZl9mbF9hcCwgYnkgPSBjKCJGb2NhbExlbmd0aCIgPSAiRm9jYWxMZW5ndGhfYmluIiwgIkFwZXJ0dXJlIiA9ICJBcGVydHVyZV9iaW4iKSkgJT4lIAogIG11dGF0ZShDb3VudCA9IGlmZWxzZShpcy5uYShDb3VudCksIDAsIENvdW50KSkKCmhlYXRtYXBfcGxvdCA8LSBnZ3Bsb3QoaGVhdG1hcF9mbF9hcCwgYWVzKHggPSBGb2NhbExlbmd0aCwgeSA9IEFwZXJ0dXJlKSkgKyAKICBnZW9tX3Jhc3RlcihhZXMoZmlsbCA9IENvdW50KSwgaW50ZXJwb2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSB4X2JyZWFrcykgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSByZXYoYnJld2VyLnBhbCg1LCAiUmRZbEJ1IikpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKCmBgYAoKYGBge3IgUGxvdCBIZWF0bWFwfQpoZWF0bWFwX3Bsb3QKYGBgCgpXZWxsIGlmIEkgd2FzIGxvb2tpbmcgZm9yIGFuIGluZGljYXRpb24gb2YgbXkgY29uc3RyYWludHMgSSB0aGluayBJIGZvdW5kIGl0LiBUaGUgKnZhc3QqIG1ham9yaXR5IG9mIG15IHBob3RvcyBzZWVtIHRvIGJlIHRha2VuIGF0IDE2bW0gb24gdGhlIGtpdCBsZW5zOgoKYGBge3IgcGxvdCBoaXN0b2dyYW0sIGZpZy5oZWlnaHQ9MSwgZmlnLndpZHRoPTR9CmZsX2FwZXJ0dXJlX2FjdCAlPiUKICBmaWx0ZXIoTW9kZWwgPT0gJ0E2MDAwICgrU29ueSAxNi01MG1tIGYzLjUtNS42KScpICU+JQogIGdyb3VwX2J5KEZvY2FsTGVuZ3RoKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKEZvY2FsTGVuZ3RoKSkpICsgCiAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKS9zdW0oLi5jb3VudC4uKSkpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogIHlsYWIoJ1BlcmNlbnQgb2YgcGhvdG9zICglKScpCmBgYAoKVGhlIHJlbGF0aXZlIHByb3BvcnRpb25zIGhlcmUgYXJlIGFsbW9zdCByZWRpY3Vsb3VzLCBzbyBJIGd1ZXNzIEkgaGF2ZSBteSBhbnN3ZXIg8J+YjgoKIyMjIFNvbWUgZmluYWwgcmVtYXJrcwoKVGhlcmUncyBhIGxvdCBtb3JlIHRvIGRpZyBpbnRvIGhlcmUuIEZvciBvbmUsIEkgZGVsaWJlcmF0ZWx5IGtlZXAgbXkgYXBlcnR1cmUgaW4gdGhlIDUuNiAtIDggcmFuZ2UgYmVjYXVzZSB0aGF0J3Mgd2hlcmUgSSBnZXQgdGhlIGJlc3QgaW1hZ2UgcXVhbGl0eSwgc28gc2VlaW5nIGEgbGFyZ2UgbnVtYmVyIG9mIHNob3RzIGF0IDMuNSBzdGlsbCByYWlzZXMgbXkgZXllYnJvdyBzbGlnaHRseS4gTWF5YmUgSSBzaG91bGQgZ2V0IGEgd2lkZSBhbmdsZSBsZW5zIHdpdGggYSBsYXJnZSBhcGVydHVyZT8gU291bmRzIGV4cGVuc2l2ZSAuLi4KCk9uIHRoZSBkYXRhIGZyb250IHRob3VnaCwgdGhlcmUncyBhIGxvdCBtb3JlIG9uIG9mZmVyIGluIGV4aWZ0b29sLiBJIGhhdmVuJ3QgbG9va2VkIGF0IGhvdyBJIGNvdWxkIGJldHRlciB1c2UgSVNPIG9yIGhvdyBteSBwaG90b2dyYXBoeSBzdHlsZSBoYXMgY2hhbmdlZCBvdmVyIHRpbWUsIG5leHQgdGltZSAuLi4KCgpbXjFdOiBJIGhhdmUgY29ycmVjdGVkIGJvdGggdGhlIGFwZXJ0dXJlIGFuZCBmb2NhbCBsZW5ndGggY2FsY3VsYXRpb25zIGZvciB0aGUgZWZmZWN0IG9mIHNlbnNvciBzaXplIHRvIGdldCBhbiBhcHBsZXMgdG8gYXBwbGVzIGNvbXBhcmlzb24gYmV0d2VlbiB0aGUgU29ueSBhbmQgdGhlIE9seW1wdXMuIEluIHRoaXMgY2FzZSB0aGUgbWljcm8gNC8zIG51bWJlcnMgd2VyZSBtdWx0aXBsaWVkIGJ5IDIvMS41MyB0byBjb252ZXJ0IHRoZW0gdG8gYW4gQVBTLUMgZXF1aXZhbGVudCBudW1iZXIu